#!/bin/sh

VERBOSITY=0
CONFIG=/etc/cirros-init/configdrive
NAME="${0##*/}"
LABELS="config-2 CONFIG-2"
SEED_PRE_D="/var/lib/cloud/seed/configdrive-pre"
SEED_POST_D="/var/lib/cloud/seed/configdrive"

. ${CIRROS_SHLIB:=/lib/cirros/shlib} ||
	{ echo "failed to read ${CIRROS_SHLIB}" 1>&2; exit 1; }

Usage() {
	cat <<EOF
Usage: ${0##*/} mode output_d

   Datasource for openstack config drive
EOF
}

search_local() {
	local out_d="$1"
	local devlist="" num="" found="" fstree_d=""
	local raw_d="" dev="" rdir="" mdjson="" ud="" found=""
	local label=""
	for label in $LABELS; do
		find_devs_with "LABEL=$label" && devlist=${_RET} && break
	done
	[ -n "$devlist" ] || {
		debug 1 "no devices with a label in $LABELS";
		exit 0;
	}

	if [ -d "${SEED_PRE_D}" ]; then
		devlist="$SEED_PRE_D $devlist"
	fi
	if [ -d "${SEED_POST_D}" ]; then
		devlist="$devlist $SEED_POST_D"
	fi

	num=0
	for dev in ${devlist}; do num=$(($num+1)); done
	[ $num -eq 1 ] || debug 1 "multiple devices matching $label: $devlist"

	[ -d "$out_d" ] || mkdir -p "$out_d" ||
		fail "failed to create outputdir: ${out_d}"

	found=""
	rdir=""
	fstree_d="${out_d}/processed"
	raw_d="${out_d}/raw"
	for dev in ${devlist}; do
		rdir="${raw_d}.tmp"
		rm -Rf "$rdir"
		if [ -b "$dev" ]; then
			mount_callback_umount "$dev" -o,ro cp -a "${rdir}" ||
				{ debug 1 "mount callback umount $dev failed"; continue; }
		else
			cp -a "$dev" "${rdir}" ||
				{ debug 1 "failed to copy from $dev"; continue; }
		fi

		mdjson="$rdir/openstack/latest/meta_data.json"
		if [ -f "$mdjson" ]; then
			json2fstree "$fstree_d" "$mdjson" ||
				fail "json2fstree failed on $mdjson for $dev"
			ud="$rdir/openstack/latest/user_data" 
			[ -f "$ud" ] && cp "$ud" "$fstree_d/user-data"
			found="$dev"
			mv "$rdir" "$raw_d" ||
				fail "rename failed!"
			break
		fi
	done

	[ -z "$found" ] && return 0

	# now we have filesystem rendering at $fstree_d
	# and raw data (copy of config drive data) at $raw_d
	mkdir -p "${out_d}/data" ||
		fail "failed to make data dir"

	start_d="$PWD"
	cd "${out_d}/data"
	local f="" t="" fix=""
	set +f
	for f in ../processed/*; do
		[ -f "$f" ] || continue
		ln -s "$f" .
	done
	set -f

	if [ -d ../processed/public_keys ]; then
		set +f
		cat ../processed/public_keys/* > public-keys || rm public-keys
		set -f
	fi

	for fix in uuid:instance-id hostname:local-hostname user_data:user-data \
		availability_zone:availability-zone launch_index:launch-index; do
		f="${fix%:*}"
		t="${fix#*:}"
		[ -f "$f" -a ! -f "$t" ] || continue
		ln -sf "$f" "$t" || fail "failed to link $f to $t"
	done

	# now create files/ tree
	cd "${start_d}"
	cd "${out_d}"
	local d path content_path omask
	omask=$(umask)
	umask 0226
	if [ -f processed/files/0 ]; then
		# processed/files/0 is the length
		set +f
		for d in processed/files/*; do
			[ "${d##*/}" = "0" ] && continue
			set -f
			[ -f "$d/path" -a -f "$d/content_path" ] ||
				{ debug 1 "$d unexpected file dir"; continue; }
			{ read path < "$d/path" || [ -n "$path" ] ; } &&
				{ read content_path < "$d/content_path" ||
				  [ -n "$content_path" ]; } ||
				{ debug 1 "skipping $d, failed reads"; continue; }
			mkdir -p "files/${path%/*}" &&
				cp "raw/openstack/${content_path}" "files/${path}" ||
				{ fail "failed to create ${path} in files"; return 1; }
		done
		set -f
	fi

	umask $omask

	cd "$start_d"
	echo 0 > "$out_d/result"
}

apply() {
	local mode="$1" data_d="$2"
	if [ -d "$data_d/files" ]; then
		local omask="" f="" path="" tpath=""
		omask=$(umask)
		umask 0226
		for f in $(find "$data_d/files/" -type f); do
			path="${f#${data_d}/files/}"
			tpath="${TARGET_ROOT}/${path}"
			mkdir -p "${tpath%/*}" && cp "${f}" "${tpath}" ||
				{ error "failed to create ${tpath}"; return 1; }
		done
		umask "$omask"
	fi
}

short_opts="hv"
long_opts="help,verbose"
getopt_out=$(getopt --name "${0##*/}" \
	--options "${short_opts}" --long "${long_opts}" -- "$@") &&
	eval set -- "${getopt_out}" ||
	bad_Usage

while [ $# -ne 0 ]; do
	cur=${1}; next=${2};
	case "$cur" in
		-h|--help) Usage ; exit 0;;
		-v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
		--) shift; break;;
	esac
	shift;
done

[ $# -eq 2 ] || bad_Usage "must provide mode and output dir"
mode="$1"
out_d="$2"

[ "$mode" = "local" -o "$mode" = "apply-local" ] ||
	{ debug 2 "only supported in mode 'local'"; exit 0; }

[ ! -e "$CONFIG" ] || . "$CONFIG" ||
	fail "failed to read $CONFIG"

if [ "$mode" = "local" ]; then
	search_local "$out_d"
elif [ "$mode" = "apply-local" ]; then
	apply "$mode" "$out_d"
else
	fail "error, unexpected input"
fi

exit
# vi: ts=4 noexpandtab
